Next | Prev | Up | Top | Contents | Index

Shared Data between Upper-half and Interrupt Routines

If the upper-half and interrupts use biowait and biodone for synchronization, the driver will perform as desired on both single-processor and multiprocessor systems. This is because these routines have already been made multiprocessor-safe. Often, however, an interrupt routine will synchronize with upper-half procedures by using the sleep/wakeup functions and a shared flag word. The following is a common scenario:

Upper-half routine:

   s = splvme();
   flag |= WAITING;
   while (flag & WAITING) {
      sleep(&flag, PZERO);
   }
   splx(s);
Interrupt routine:

   if (flag & WAITING) {
      wakeup(&flag);
      flag &= ~WAITING;
   }
The splvme call is used to protect flag from being modified in an interrupt routine. The splvme call raises the interrupt priority level only on the current processor and is, thus, insufficient on a multiprocessor. In this case, semaphores can be used for synchronization. When you initialize a semaphore to 0, the first psema call puts the calling process to sleep. A subsequent vsema(D3X) call will put the process back on the run queue. See the spl(D3) man page.The following code can replace the upper-half and interrupt scenario above:

Initialization function:

initnsema(&driversema, 0, "driver");

Upper-half routine:

psema(&driversema, PZERO);

Interrupt routine:

vsema(&driversema);

Since the semaphore functions themselves are multiprocessor-safe, no additional locking is necessary.

There may be other cases within the driver where data not specifically pertaining to synchronization is shared between an upper-half routine and an interrupt routine. You can identify these cases easily by searching for splN/splx calls and identifying the data actually being protected against concurrent access. In such cases, it is often useful to employ spinlocks. (They are called spinlocks because the locking functions actually loop until a test-and-set value is unset.) Replace the splN call with a LOCK(D3) call, and replace the splx call with a corresponding UNLOCK(D3) call. These replacements allow the driver to perform as desired on a single processor while providing locking on a multiprocessor. See the spl(D3) man page.

Caution: Data and cache interactions must be considered.Variables that might be in registers must be declared volatile and protected as well in multiprocessor device drivers.



Next | Prev | Up | Top | Contents | Index